#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

# Copyright (c) 2005-2014 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import os
import sys
import json
import bbki
import platform


def usage():
    print('Usage:')
    print('   bbki show <config-string>')
    print('   bbki build <config-string>')
    print('   bbki clean <config-string>')
    print('   bbki check <config-string>')


def die(msg):
    print(msg, file=sys.stderr)
    sys.exit(1)


def doShow(cfg):
    bbkiObj = bbki.Bbki(cfg)

    be = bbkiObj.get_pending_boot_entry()
    if be is None:
        be = "None"
    else:
        be = "Linux (%s)" % (be.postfix)
    print("Main Boot Entry: %s" % (be))
    print("")

    be = bbkiObj.get_newest_boot_entry()
    if be is None:
        be = "None"
    else:
        be = "Linux (%s)" % (be.postfix)
    print("Newest Boot Entry: %s" % (be))
    print("")

    print("All Boot Entries:")
    for be in bbkiObj.get_boot_entries():
        print("    Linux (%s)" % (be.postfix))
    print("")

    print("All History Boot Entries:")
    for be in bbkiObj.get_history_boot_entries():
        print("    Linux (%s)" % (be.postfix))
    print("")

    print("Stable: %s" % ("yes" if bbkiObj.get_stable_flag() else "no"))
    print("")


def doBuild(cfg):
    bbkiObj = bbki.Bbki(cfg)

    bootEntrySpec = bbki.BootEntrySpec({
        "arch": platform.machine(),
        "kernel_type": cfg.spec_get_kernel()[0],
        "kernel_params": cfg.spec_get_kernel()[1],
        "kernel_addon_params": cfg.spec_get_kernel_addons(),
        "initramfs_params": {},
    })

    print("        - Synchronizing...")
    bbkiObj.sync(bootEntrySpec)

    print("        - Fetching...")
    bbkiKernelBuilder = bbkiObj.get_kernel_installer(bootEntrySpec)
    bbkiKernelBuilder.fetch_kernel()
    for name in bbkiKernelBuilder.spec.kernel_addons:
        bbkiKernelBuilder.fetch_kernel_addon(name)

    print("        - Extracting...")
    bbkiKernelBuilder.unpack()

    print("        - Patching...")
    bbkiKernelBuilder.patch_kernel()

    print("        - Generating .config file...")
    bbkiKernelBuilder.generate_kernel_config_file()

    print("        - Building and installing...")
    bbkiKernelBuilder.install_kernel()

    if cfg.flags & bbki.Flags.INITRAMFS:
        print("        - Creating initramfs...")
        bbkiKernelBuilder.install_initramfs()


def doClean(cfg):
    # FIXME
    assert False


def doCheck(cfg):
    # FIXME
    assert False


class JsonConfig(bbki.ConfigBase):

    def __init__(self, s):
        self._data = json.loads(s)

    @property
    def flags(self):
        if "flags" in self._data:
            if self._data["flags"].lower() == "k":
                return bbki.Flags.KERNEL
            elif self._data["flags"].lower() == "ki":
                return bbki.Flags.KERNEL | bbki.Flags.INITRAMFS
            elif self._data["flags"].lower() == "bki":
                return bbki.Flags.BOOTLOADER | bbki.Flags.KERNEL | bbki.Flags.INITRAMFS
            elif self._data["flags"].lower() == "bbki":
                return bbki.Flags.BIOS | bbki.Flags.BOOTLOADER | bbki.Flags.KERNEL | bbki.Flags.INITRAMFS
            else:
                assert False
        else:
            return bbki.Flags.BIOS | bbki.Flags.BOOTLOADER | bbki.Flags.KERNEL | bbki.Flags.INITRAMFS

    def get_fs_layout(self):
        # FIXME
        return bbki.FsLayout()

    def get_build_variable(self, var_name):
        if var_name in "MAKEOPTS":
            makeOpts = ""
            if "build_options" in self._data:
                if "jobs" in self._data["build_options"]:
                    makeOpts += "--jobs=%d " % (self._data["build_options"]["jobs"])
                if "loadavg" in self._data["build_options"]:
                    makeOpts += "--load-average=%d " % (self._data["build_options"]["loadavg"])
                makeOpts = makeOpts.rstrip()
            return makeOpts

        return ""

    def get_mount_points(self):
        ret = []
        for item in self._data.get("mount_points", []):
            assert item["underlay_disk"]["type"] == "image-file-on-removable"
            ret.append(bbki.HostMountPoint(item))
        return ret

    def get_system_init(self):
        value = self._data.get("system_init", None)
        if value == "openrc":
            return bbki.SystemInit(bbki.SystemInit.Type.OPENRC)
        elif value == "systemd":
            return bbki.SystemInit(bbki.SystemInit.Type.SYSTEMD)
        else:
            raise Exception("Invalid value in \"system_init\": %s" % (value))

    def get_core_dump_handler(self):
        # FIXME
        return bbki.CoreDumpHandler(bbki.CoreDumpHandler.Type.NONE)

    def get_online_mount_points(self):
        assert False

    def get_boot_mode(self):
        value = self._data.get("boot_mode", None)
        if value == "efi":
            return bbki.BootMode.EFI
        elif value == "bios":
            return bbki.BootMode.BIOS
        else:
            raise Exception("Invalid value in \"boot_mode\": %s" % (value))

    def get_bootloader_extra_time(self):
        # FIXME
        return 0

    def get_kernel_extra_init_cmdline(self):
        # FIXME
        return ""

    def spec_get_kernel(self):
        value = self._data.get("kernel", {})
        if len(value) == 0:
            return ("linux", {})
        elif len(value) == 1:
            for kernel_type, kernel_params in value.items():
                return (kernel_type, kernel_params)
        else:
            raise Exception("Invalid value in \"kernel\": %s" % (value))

    def spec_get_kernel_addons(self):
        return self._data.get("kernel_addons", {})


if __name__ == '__main__':
    if len(sys.argv) <= 1:
        usage()
        sys.exit(1)

    if sys.argv[1] == "--help":
        usage()
        sys.exit(0)

    if os.getuid() != 0:
        die("You must run this command as root!")

    if len(sys.argv) < 3:
        die("No config string specified!")

    op = sys.argv[1]
    cfg = JsonConfig(sys.argv[2])
    if op == "show":
        doShow(cfg)
    elif op == "build":
        doBuild(cfg)
    elif op == "clean":
        doClean(cfg)
    elif op == "check":
        doCheck(cfg)
    else:
        die('Invalid operation.')
